use axum::{
    http::{header, StatusCode},
    response::{IntoResponse, Response},
    Json,
};
use serde_json::json;
use thiserror::Error;

#[derive(Error, Debug)]
pub enum AppError {
    #[error("Database error: {0}")]
    Database(#[from] sea_orm::DbErr),

    #[error("Authentication error: {0}")]
    Auth(String),

    #[error("Unauthorized: {0}")]
    Unauthorized(String),

    #[error("Authentication redirect: {0}")]
    AuthRedirect(String),

    #[error("Authorization error: {0}")]
    Authorization(String),

    #[error("Validation error: {0}")]
    Validation(String),

    #[error("Bad request: {0}")]
    BadRequest(String),

    #[error("Conflict: {0}")]
    Conflict(String),

    #[error("Forbidden: {0}")]
    Forbidden(String),

    #[error("Not found: {0}")]
    NotFound(String),

    #[error("IO error: {0}")]
    Io(#[from] std::io::Error),

    #[error("JWT error: {0}")]
    Jwt(#[from] jsonwebtoken::errors::Error),

    #[error("Serialization error: {0}")]
    Serialization(#[from] serde_json::Error),

    #[error("YAML error: {0}")]
    Yaml(#[from] serde_yaml::Error),

    #[error("Bcrypt error: {0}")]
    Bcrypt(#[from] bcrypt::BcryptError),

    #[error("Regex error: {0}")]
    Regex(#[from] regex::Error),

    #[error("Multipart error: {0}")]
    Multipart(#[from] axum::extract::multipart::MultipartError),

    #[error("Not implemented: {0}")]
    NotImplemented(String),

    #[error("Internal server error: {0}")]
    Internal(String),
}

impl IntoResponse for AppError {
    fn into_response(self) -> Response {
        let (status, error_message) = match self {
            AppError::Database(err) => {
                tracing::error!("Database error: {:?}", err);
                (
                    StatusCode::INTERNAL_SERVER_ERROR,
                    "Database error occurred".to_string(),
                )
            }
            AppError::Auth(msg) => (StatusCode::UNAUTHORIZED, msg),
            AppError::Unauthorized(msg) => (StatusCode::UNAUTHORIZED, msg),
            AppError::AuthRedirect(msg) => {
                // Parse the error message to extract the redirect path
                // Format: "Error message:path"
                let parts: Vec<&str> = msg.splitn(3, ':').collect();
                let redirect_path = if parts.len() >= 3 {
                    parts[2] // The path is the third part
                } else {
                    "/dashboard" // Default fallback
                };

                let redirect_url =
                    format!("/login?redirect={}", urlencoding::encode(redirect_path));
                return (
                    StatusCode::FOUND,
                    [(header::LOCATION, redirect_url.as_str())],
                )
                    .into_response();
            }
            AppError::Authorization(msg) => (StatusCode::FORBIDDEN, msg),
            AppError::Validation(msg) => (StatusCode::BAD_REQUEST, msg),
            AppError::Conflict(msg) => (StatusCode::CONFLICT, msg),
            AppError::BadRequest(msg) => (StatusCode::BAD_REQUEST, msg),
            AppError::Forbidden(msg) => (StatusCode::FORBIDDEN, msg),
            AppError::NotFound(msg) => (StatusCode::NOT_FOUND, msg),
            AppError::Io(err) => {
                tracing::error!("IO error: {:?}", err);
                (
                    StatusCode::INTERNAL_SERVER_ERROR,
                    "File system error occurred".to_string(),
                )
            }
            AppError::Jwt(err) => {
                tracing::error!("JWT error: {:?}", err);
                (
                    StatusCode::UNAUTHORIZED,
                    "Invalid authentication token".to_string(),
                )
            }
            AppError::Serialization(err) => {
                tracing::error!("Serialization error: {:?}", err);
                (
                    StatusCode::INTERNAL_SERVER_ERROR,
                    "Data serialization error".to_string(),
                )
            }
            AppError::Yaml(err) => {
                tracing::error!("YAML error: {:?}", err);
                (StatusCode::BAD_REQUEST, "Invalid YAML format".to_string())
            }
            AppError::Bcrypt(err) => {
                tracing::error!("Bcrypt error: {:?}", err);
                (
                    StatusCode::INTERNAL_SERVER_ERROR,
                    "Password processing error".to_string(),
                )
            }
            AppError::Regex(err) => {
                tracing::error!("Regex error: {:?}", err);
                (
                    StatusCode::INTERNAL_SERVER_ERROR,
                    "Pattern matching error".to_string(),
                )
            }
            AppError::Multipart(err) => {
                tracing::error!("Multipart error: {:?}", err);
                (StatusCode::BAD_REQUEST, "File upload error".to_string())
            }
            AppError::NotImplemented(msg) => (StatusCode::NOT_IMPLEMENTED, msg),

            AppError::Internal(msg) => {
                tracing::error!("Internal server error: {}", msg);
                (
                    StatusCode::INTERNAL_SERVER_ERROR,
                    "Internal server error".to_string(),
                )
            }
        };

        let body = Json(json!({
            "detail": error_message
        }));

        (status, body).into_response()
    }
}

pub type Result<T> = std::result::Result<T, AppError>;
